import type { AxiosResponse } from 'axios';
import type { IRequestConfig } from './types';
import { hideFullScreenLoading, showFullScreenLoading } from './helper';
import { getToken } from '../utils/auth';
import axios from 'axios';
import { ResultEnum } from '@/enums';

/**
 * 拦截器
 */
export default class Interceptors {
  /**
   * 缓存请求对象，用于取消重复请求
   */
  private cacheRequestPromise: Record<string, Function> = {};

  /**
   * 请求拦截器 - 处理全局 loading
   * @param config IRequestConfig
   */
  requestDefaultLoading(config: IRequestConfig) {
    // 显示 loading
    if (config.loading) {
      const message = config.loadingMessage;
      showFullScreenLoading(message);
    }
    return config;
  }

  /**
   * 请求拦截器 - 处理重复请求
   * @param config IRequestConfig
   */
  requestDuplicateCancel(config: IRequestConfig) {
    let cacheRequestKey = config.url!;

    const _data = Object.assign({}, config.data);
    const _params = Object.assign({}, config.params);

    // 一般列表查询接口加上 isCancelDuplicateUrlRequests ，则缓存 key = url + method，用于切换分页时候取消上一次请求
    if (config.isCancelDuplicateUrlRequests) {
      cacheRequestKey += config.method;
    } else {
      if (JSON.stringify(_data) !== '{}') {
        cacheRequestKey += `&${JSON.stringify(_data)}`;
      }
      if (JSON.stringify(_params) !== '{}') {
        cacheRequestKey += `&${JSON.stringify(_params)}`;
      }
    }

    // 以 url + method/data 为 key 缓存请求
    config.cacheRequestKey = cacheRequestKey;

    // 取消请求的控制器
    if (!config.controller) {
      config.controller = new AbortController();
    }
    config.signal = config.controller.signal;

    // 取消重复请求
    if (this.cacheRequestPromise[cacheRequestKey] && !config.isNotCancel) {
      console.warn(`Cancel operation for [${config.method}](${config.url})`);

      // 调用里面的 abort 方法
      this.cacheRequestPromise[cacheRequestKey]?.();
      // 删除 key
      // delete this.cacheRequestPromise[cacheRequestKey];
      Reflect.deleteProperty(this.cacheRequestPromise, cacheRequestKey);
    }

    // 储存 abort 方法
    this.cacheRequestPromise[cacheRequestKey] = () => {
      config.controller?.abort();
    };

    return config;
  }

  /**
   * 请求拦截器 - 处理请求 url 域名拼接
   * @param config IRequestConfig
   */
  requestConfigUrl(config: IRequestConfig) {
    // 处理完整的 URL. 非 http, https 的才处理
    const isExternal = /^(https?:)/.test(config.url!);
    if (!isExternal) {
      // const hasServer = !!this.systemDomainMap[config.server!];
      // if (!hasServer) {
      //   console.warn(`API_BASE not found 'server: ${config.server}' config, will reset server to 'baidu'`);
      //   config.server = DOMAIN_SYSTEM_ENUM.BAIDU;
      // }
      // config.url = `${config.server}${config.url}`;
      // console.log(config.url)
    }
    return config;
  }

  // 请求参数处理
  requestParams(config: IRequestConfig) {
    const method = config.method!.toLowerCase();

    let keyName: 'params' | 'data' = 'params';
    if (['post', 'put', 'delete'].includes(method)) {
      keyName = 'data';
    }
    // 递归删除 null & '' & undefined 字段 formData 不处理
    const data = config[keyName];
    if (data instanceof FormData) {
      return config;
    }

    return config;
  }

  /**
   * 请求拦截器 - 添加header参数
   * @param config IRequestConfig
   */
  requestHeader(config: IRequestConfig) {
    const account =
      config.account ||
      (() => {
        /**
         * 修复在 vite 开发模式下状态丢失问题
         * 实际产品模式下不会有任何影响
         */
        const token = getToken();
        return Object.assign({}, config.account, { token });
      })();

    // 如果 Authorization 设置为 no-auth，则不携带 Token，用于登录、刷新 Token 等接口
    if (config.headers.Authorization !== 'no-auth' && account.token) {
      config.headers.Authorization = account.token;
    } else {
      delete config.headers.Authorization;
    }

    return config;
  }

  /**
   * 响应拦截器 - 响应成功 则 删除重复请求 key
   * @param response AxiosResponse
   */
  responseDuplicateCancel(response: AxiosResponse) {
    const requestConfig: IRequestConfig = response.config;
    Reflect.deleteProperty(this.cacheRequestPromise, requestConfig.cacheRequestKey!);
    return response;
  }

  /**
   * 响应拦截器 - 默认拦截器 处理 loading
   * @param response AxiosResponse
   */
  responseDefaultLoading(response: AxiosResponse): AxiosResponse {
    const requestConfig: IRequestConfig = response.config;
    if (requestConfig.loading) {
      hideFullScreenLoading();
    }
    return response;
  }

  /**
   * 响应拦截器 - 判断业务响应是否 成功
   * @param response AxiosResponse
   */
  responseSuccess(response: AxiosResponse): AxiosResponse<any> | Promise<AxiosResponse<any>> {
    const responseType = response.config.responseType?.toLowerCase();
    if (responseType && ['blob', 'arraybuffer', 'stream'].includes(responseType)) {
      return response;
    }

    const { code } = response.data;
    if (code === ResultEnum.SUCCESS) {
      return response;
    }

    return response;
  }

  /**
   * 响应拦截器 - 处理响应异常（业务异常）
   * @param response AxiosResponse
   */
  async responseError(response: AxiosResponse): Promise<AxiosResponse<any>> {
    const { data } = response;
    const showErrorMessage = (response.config as IRequestConfig)?.showErrorMessage;

    const { code, msg } = data;
    if (code !== ResultEnum.SUCCESS) {
      // 没有登录 跳转到登录页面
      // if (this.jumpLoginCodeList.includes(code)) {
      //   ElMessage.error(message || '会话过期， 请重新登陆!');
      //   const isExpiration = true;
      //   await this.logout(isExpiration);
      //   // redirect to login
      //   const loginUrl = this._systemDomainMap[DOMAIN_SYSTEM_ENUM.sso];
      //   if (!loginUrl) {
      //     ElMessage.error('找不到登陆页地址');
      //     return Promise.reject(data);
      //   }
      //   jumpSSOLogin(loginUrl);
      //   return Promise.reject(data);
      // }

      // 没有权限 跳转到404
      if (code === 403) {
        // Router.replace('/403');
        return Promise.reject(data);
      }
      if (code === 401) {
        ElMessage.error(msg || '没有权限访问改系统，请使用正确的账号登录');
        // await sleep(1500);
        // this._logout(true);
        return Promise.reject(data);
      }

      if (showErrorMessage) {
        ElMessage.error(msg || '');
      }
      return Promise.reject(data);
    }
    return response;
  }

  /**
   * 响应拦截器 - 处理响应失败拦截器 (http异常&canceled)
   * @param error any
   */
  async handleError(error: any): Promise<any> {
    hideFullScreenLoading();
    // 通过 axios.isCancel(err) 判断是否是 canceled的请求
    if (axios.isCancel(error)) {
      return Promise.reject({
        isCancel: true,
        status: 'canceled',
        statusText: 'Cancel operation',
        message: error.message,
      });
    }
    if (error.response) {
      const messages: Record<string, any> = {
        400: '错误请求',
        401: '会话过期，请重新登录',
        403: '拒绝访问',
        404: '请求错误，未找到该资源',
        405: '请求方法未允许',
        408: '请求超时',
        500: '服务器端出错',
        501: '网络未实现',
        502: '网络错误',
        503: '服务不可用',
        504: '网络超时',
        505: 'http版本不支持该请求',
      };
      error.message = messages[error.response.status] || `连接错误${error.response.status}`;
    } else {
      error.message = error.message || '连接到服务器失败';
    }

    try {
      const { status, statusText } = error.response ?? {};
      // 登录过期
      if (status === 401) {
        ElMessage.closeAll();
        // ElMessage.error(error.message);
        // const isExpiration = true;
        // await this.logout(isExpiration);
        // // redirect to login
        // const loginUrl = this._systemDomainMap[DOMAIN_SYSTEM_ENUM.sso];
        // if (!loginUrl) {
        //   ElMessage.error('找不到登陆页地址');
        //   return Promise.reject(error);
        // }
        // jumpSSOLogin(loginUrl);
        return Promise.reject(error);
      }

      // 非登录过期错误
      ElMessage.error(`${status} - ${error.message}`);

      return Promise.reject({
        status,
        statusText,
        message: error.message,
      });
    } catch (err) {
      console.warn(err);
      return Promise.reject({
        status: '',
        statusText: '未知错误',
        message: error.message,
      });
    }
  }
}
